package org.ccay.mail.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import org.ccay.core.base.KeyValuePairVO;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.exception.impl.CommonApplicationException;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.orm.NoTransation;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.util.CollectionUtil;
import org.ccay.core.util.EncodingUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.core.util.TemplateUtil;
import org.ccay.hypertext.MailTemplate;
import org.ccay.hypertext.service.IHyperTextService;
import org.ccay.mail.MailApplicationException;
import org.ccay.mail.MailVO;
import org.ccay.mail.MeetingMailVO;
import org.ccay.mail.service.IMailService;
import org.ccay.mail.service.impl.internal.MailConfigBean;
import org.ccay.mail.service.impl.internal.MailUtil;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.MailSendException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

import freemarker.template.TemplateException;

/**
 * 基础邮件服务	
 * @author chaowangbang
 * @since 2011-11-21
 */
public class BaseMailService implements IMailService {
	private static final ILogger logger = CcayLoggerFactory.getLogger("CcayMail");
	private static final String NOTADDRESSERROR = "notAddressError";
	/**
	 * 邮件配置
	 */
	private MailConfigBean mailConfig;

	/**
	 * 邮件发送器
	 */
	private JavaMailSenderImpl mailSender;	
	
	/**
	 * 最大容许错误次数
	 */
	private static int sendFailedMailCount = 10;
	
	/**
	 * 邮件模板
	 */
	@Inject
	@Named("mailTemplateHyperTextService")
	private IHyperTextService<MailTemplate> templateService;
	
	@Inject
	private Environment environment;
	
	/* (non-Javadoc)
	 * @see org.ccay.mail.service.IMailService#sendMail(org.ccay.mail.MailVO)
	 */
	@NoTransation
	public void sendMail(MailVO newMail) throws ApplicationException {
		MailUtil.checkMail(newMail); 
		MimeMessageHelper messageHelper = null;
		//TODO  fas all
		try {
			messageHelper = this.getMimeMessageHelper(newMail);
			this.setReceivers(newMail, messageHelper); 
			this.allSendMail(messageHelper,newMail);
		} catch (MessagingException e) {
			logger.error(e);
		} catch (UnsupportedEncodingException e) {
			logger.error(e);
		} catch (MailApplicationException e) {
			 if(!e.getErrorCode().equals(NOTADDRESSERROR)){
				logger.error(e);
				return;
			 }
			 //执行单个发送
			 try {
				batchSingleSendMail(newMail, messageHelper);
			} catch (MessagingException e1) {
				logger.error(e1);
			}
		}
		
	}
	
	
	/* (non-Javadoc)
	 * @see org.ccay.mail.service.IMailService#sendMeetingMail(org.ccay.mail.MeetingMailVO)
	 */
	public void sendMeetingMail(MeetingMailVO meetingMailVO) throws ApplicationException{
		
	}    
	
	
	/**
	 * 删除所有无效的地址重新发
	 * @param newMail
	 * @param errorMsg
	 * @throws MailApplicationException 
	 */
	private void removeAllInvaldateEmail(MailVO newMail,String errorMsg) throws MailApplicationException{
		
		newMail.setTo(removeInvalidateEmail(newMail.getTo(),errorMsg) );
		newMail.setCc(removeInvalidateEmail(newMail.getCc(),errorMsg) );
		newMail.setBcc(removeInvalidateEmail(newMail.getBcc(),errorMsg) );
		//清除过错误邮件地址后  再次验证是否有收件人
		if(CollectionUtil.isNullOrEmpty(newMail.getTo()) && CollectionUtil.isNullOrEmpty(newMail.getCc()) && CollectionUtil.isNullOrEmpty(newMail.getBcc())){
			//无收件人
			throw new MailApplicationException("ccay.mail.00010004");
		}	 
	}
	/**
	 * 删除邮件数组里面的错误地址
	 * @param emails
	 * @param errorMsg
	 * @return
	 */
	private String[] removeInvalidateEmail(String[] emails,String errorMsg){
		if(CollectionUtil.isNullOrEmpty(emails)){
			return new String[0];
		}
		List<String> newEmails =  new ArrayList<String>(); 
		 //删除错误的邮
		 for(String email :emails){
			 if(errorMsg.indexOf(" "+email) ==-1){
				 newEmails.add(email);			 
			 }
		 }
		 return newEmails.toArray(new String[newEmails.size()]);
	}
	/** 
	 * 	
	 * @author chaowangbang
	 * @since 2012-7-27
	 * @param messageHelper
	 * @param newMail
	 * @throws MessagingException 
	 * @throws UnsupportedEncodingException 
	 */
	private void setAttachments(MimeMessageHelper messageHelper, MailVO newMail) throws MessagingException, UnsupportedEncodingException,ApplicationException {
		if (newMail.getAttachmentList().isEmpty() && newMail.getAttachments().isEmpty()){
			return ;
		}
		logger.debug("Attachments:" + (newMail.getAttachments().size() + newMail.getAttachmentList().size()));
		String fileName = null;
		String filePath = null;
		for(Map.Entry<String,String> entry : newMail.getAttachments().entrySet()){
			fileName = entry.getKey();
			filePath = entry.getValue();
			//验证附件是否有效附件 如果不是 那么不发送邮件
			this.addAttachment(fileName, filePath, messageHelper);
		}
		
		for(KeyValuePairVO keyValuePair : newMail.getAttachmentList()){
			fileName = keyValuePair.getKey();
			filePath = keyValuePair.getValue();
			//验证附件是否有效附件 如果不是 那么不发送邮件
			this.addAttachment(fileName, filePath, messageHelper);
		}
	}
	
	/**
	 * 添加附件
	 * @param fileName
	 * @param filePath
	 * @param messageHelper
	 * @throws MailApplicationException
	 * @throws UnsupportedEncodingException
	 * @throws MessagingException
	 */
	private void addAttachment(String fileName,String filePath,MimeMessageHelper messageHelper) throws MailApplicationException, UnsupportedEncodingException, MessagingException{
		if(!MailUtil.validateAttachment(filePath)){
			 logger.error("Attachments:" + fileName + " not exists!");
			 logger.error("Attachments path:" + filePath + " not exists!");
			//无效的附件 导致不发送邮件
			throw new MailApplicationException("ccay.mail.00010005");
		}
		 FileSystemResource file = new FileSystemResource(new File(filePath));
		 messageHelper.addAttachment(MimeUtility.encodeText(fileName,"UTF-8",null), file);
	}
	/** 
	 * 设置收件
	 * @param newMail
	 * @param messageHelper
	 * @throws MessagingException
	 */
	private void setReceivers(MailVO newMail, MimeMessageHelper messageHelper) throws MessagingException {
		//保证不存在上次的地址
		messageHelper.setTo(new String[0]);
		messageHelper.setCc(new String[0]);
		messageHelper.setBcc(new String[0]);
		if(newMail.getTo()!=null){
			messageHelper.setTo(newMail.getTo());
		}
		if(newMail.getCc()!=null){
			messageHelper.setCc(newMail.getCc());
		}
		if(newMail.getBcc()!=null){
			messageHelper.setBcc(newMail.getBcc());	
		}
		if(newMail.getAddresser()!=null){
			String fromName = newMail.getFromName();
			if(StringUtil.isNullOrEmpty(fromName)){
				messageHelper.setFrom(newMail.getAddresser());
			}else {
				try {
					messageHelper.setFrom(newMail.getAddresser(), fromName);
				} catch (UnsupportedEncodingException e) {
					logger.error("Can not encode from name : " + fromName, e);
					messageHelper.setFrom(newMail.getAddresser());
				}
			}
		}
	}
	
	/**
	 * 设置内容	
	 * @param messageHelper
	 * @throws MailApplicationException 
	 * @throws MessagingException 
	 * @throws ApplicationException 
	 * @throws IOException  
	 * 
	 * @author chaowangbang
	 * @param mailMessage 
	 */
	@NoTransation
	private void setSubjectAndText(MimeMessageHelper messageHelper, MailVO newMail) throws MessagingException, ApplicationException{		
		if(StringUtil.isNullOrEmpty(newMail.getTemplateName())){
			//无模板发送，直接设置
			messageHelper.setText(newMail.getContent(),true);
			messageHelper.setSubject( insertEnvTag(newMail.getSubject()));			
			return;
		} 
		TemplateUtil.putAppRoot(newMail.getBindItems());
		// 调用mailTemplateService进行查找富文本，得到模板template
		MailTemplate query = new MailTemplate();
		query.setName(newMail.getTemplateName());
		MailTemplate template = templateService.findHyperText(query);		 
	
		Map<String, Serializable> data = newMail.getBindItems();

		try {	
			if(template.getContent() == null || template.getTitle()== null){
				throw new CommonApplicationException("Subject or content of the mail template is empty.");
			}
			//邮件内容
			String finalContent=TemplateUtil.putDataIntoTemplate(template.getContent(), data);
			//邮件主题
			String finalSubject=TemplateUtil.putDataIntoTemplate(template.getTitle(), data);
			//设置主题
			messageHelper.setSubject(insertEnvTag(finalSubject));
			//设置邮件内容
			messageHelper.setText(finalContent, true);
			/**
			 * 会议邮件使用方式不同
			 */
			newMail.setSubject(insertEnvTag(finalSubject));
			newMail.setContent(finalContent);
		} catch (TemplateException e) {
			// TemplateException
			throw new MailApplicationException("ccay.mail.00010002", e);
		}catch (IOException e) {
			// IO异常
			// TemplateException
			throw new MailApplicationException("ccay.mail.00010002", e);
		} catch (RuntimeException e) {
			// TemplateException
			throw new MailApplicationException("ccay.mail.00010002", e);
		}
		
	}
	
	/**
	 * 获取MessageHelper
	 * @param mailMessage
	 * @param newMail
	 * @return
	 * @throws MessagingException 
	 * @throws UnsupportedEncodingException 
	 */
	private MimeMessageHelper getMimeMessageHelper(MailVO newMail)throws ApplicationException, MessagingException, UnsupportedEncodingException {
		MimeMessage mimeMessage = mailSender.createMimeMessage();
		MimeMessageHelper messageHelper =null;
		
		messageHelper = new MimeMessageHelper(mimeMessage,true,EncodingUtil.UTF_8);   
		messageHelper.setFrom(mailConfig.getFromAddress(),mailConfig.getFromName());
		setAttachments(messageHelper, newMail);
        setSubjectAndText(messageHelper, newMail);
        
        // begin add by zWX304259/5.4.17  设置邮件重要性[1=High|0=Normal|5=Low]
        messageHelper.setPriority(newMail.getPriority());
     	// end add by zWX304259/5.4.17
        
		return messageHelper;
	}
	
	/**
	 * 执行单个发送
	 * @param newMail
	 * @param messageHelper
	 * @throws ApplicationException
	 * @throws MessagingException 
	 */
	private void batchSingleSendMail(MailVO newMail, MimeMessageHelper messageHelper) throws ApplicationException, MessagingException{
		MailVO singleMail = null;
		for (String addresser : newMail.getTo()){
			singleMail = new MailVO();
			singleMail.setTo(new String[]{addresser});
			this.setReceivers(singleMail, messageHelper);
			this.singleSendMail(messageHelper,singleMail);
		}
		for (String addresser : newMail.getCc()){
			singleMail = new MailVO();
			singleMail.setCc(new String[]{addresser});
			this.setReceivers(singleMail, messageHelper);
			this.singleSendMail(messageHelper,singleMail);
		}
		for (String addresser : newMail.getBcc()){
			singleMail = new MailVO();
			singleMail.setBcc(new String[]{addresser});
			this.setReceivers(singleMail, messageHelper);
			this.singleSendMail(messageHelper,singleMail);
		}
	}
	
	
	/**
	 * 处理单个邮件发送
	 * @param messageHelper
	 * @param singleMail
	 * @throws ApplicationException
	 */
	private void singleSendMail(MimeMessageHelper messageHelper,MailVO singleMail) throws ApplicationException {	
		try {
			this.sendEmail(messageHelper,singleMail);
		} catch (MessagingException e) {			
			logger.error(e);
		} catch(MailSendException e){	
			logger.error(e);
		} catch(RuntimeException e){
			logger.error(e);
		}
	}
	
	
	/**
	 * 处理所有邮件发送
	 * @param newMail
	 * @return
	 * @throws ApplicationException
	 * @throws MessagingException 
	 */
	private void allSendMail(MimeMessageHelper messageHelper,MailVO newMail) throws ApplicationException, MessagingException {	
		newMail.addCount();
		try {
			this.sendEmail(messageHelper,newMail);
		}catch (MailSendException e) {
			logger.error(e);
			//如果发送错误次数 超过规定的10次 那么不发送
			if(newMail.getSendCount()>=sendFailedMailCount){
				logger.warn("mail send count > faile count:" +sendFailedMailCount + ", mail is don't send. "  );
				return;
			}
			if(checkInvaldateEmail(newMail,e.getMessage())){
				//移除无效的邮件地址
				this.removeAllInvaldateEmail(newMail,e.getMessage());
				this.setReceivers(newMail, messageHelper);
				//递归调用发送邮
				this.allSendMail(messageHelper,newMail);
			}else{
				throw new MailApplicationException(NOTADDRESSERROR,e);
			}
			
		}
		
	}
	

	
	/**
	 * 发送邮件
	 * @param messageHelper
	 * @param newMail
	 * @throws MessagingException
	 */
	private void sendEmail(MimeMessageHelper messageHelper,MailVO newMail) throws MessagingException{
		
		MimeMessage mimeMessage = messageHelper.getMimeMessage();
		
		//设置邮件头信息，以建立规则对邮件进行移动 --- y00267790/20141230
		//key 改为 IsSystemMail ---20150522
		mimeMessage.setHeader("IsSystemMail", RequestContext.getCurrent().getApplication().getAppName());
		
		if (!StringUtil.isNullOrEmpty(newMail.getHeaderKey()) && !StringUtil.isNullOrEmpty(newMail.getHeaderValue())) {
			mimeMessage.setHeader(newMail.getHeaderKey(), newMail.getHeaderValue());
		}
		
		logger.debug("Message Encoding:" + mimeMessage.getEncoding());
		logger.info("Before send mail {0} to {1}",mimeMessage.getSubject(), StringUtil.join(newMail.getTo(), ","));
		logger.info("Before send mail {0} cc {1}",mimeMessage.getSubject(), StringUtil.join(newMail.getCc(), ","));
		logger.info("Before send mail {0} Bcc {1}",mimeMessage.getSubject(), StringUtil.join(newMail.getBcc(), ","));
		mailSender.send(mimeMessage);
		logger.info("Mail is sent to mail server.");
	}
	
	/**
	 * 对错误信息判断是否存在邮件地址中
	 * @param newMail
	 * @param errorMsg
	 * @return
	 */
	private boolean checkInvaldateEmail(MailVO newMail,String errorMsg){
		//错误信息是否在主送人邮件地址中
		if(!CollectionUtil.isNullOrEmpty(newMail.getTo())){
			if(checkErrorMsg(newMail.getTo(),errorMsg)){
				return true;
			}
		}else{
			newMail.setTo(new String[0]);
		}
		
		//错误信息是否在抄送人邮件地址中
		if(!CollectionUtil.isNullOrEmpty(newMail.getCc())){
			if(checkErrorMsg(newMail.getCc(),errorMsg)){
				return true;
			}
		}else{
			newMail.setCc(new String[0]);
		}
		
		//错误信息是否在密送人邮件地址中
		if(!CollectionUtil.isNullOrEmpty(newMail.getBcc())){
			if(checkErrorMsg(newMail.getBcc(),errorMsg)){
				return true;
			}
		}else{
			newMail.setBcc(new String[0]);
		}
		return false;
	}
	
	/**
	 * 检查错误信息
	 * @param address
	 * @param errorMsg
	 * @return
	 */
	private boolean checkErrorMsg(String[] address,String errorMsg){
		for(String email : address){
			 if(errorMsg.indexOf(" "+email) !=-1){
				return true;		 
			 }
		 }
		return false;
	} 

	/**
	 * 清空所有邮件地址
	 * @param mail
	 */
	private void clearAddresser(MailVO mail){
		//清空主送人收件地址
		if(!CollectionUtil.isNullOrEmpty(mail.getTo())){
			mail.setTo(new String[0]);
		}
		//清空抄送人收件地址
		if(!CollectionUtil.isNullOrEmpty(mail.getCc())){
			mail.setCc(new String[0]);
		}
		//清空密送人收件地址
		if(!CollectionUtil.isNullOrEmpty(mail.getBcc())){
			mail.setBcc(new String[0]);
		}
	}
	/**
	 * 附加环境标识，在测试环境的邮件中自动追加类似于[Test][From SIT]之类的前缀，以便于用户识别
	 * @param subject 原始标题
	 * @return
	 */
	private String insertEnvTag(String subject) {
		String env = environment.getActiveProfiles()[0];
		if("production".equals(env)){
			return subject;
		}
		return MessageFormat.format("[Test][{0}]{1}", StringUtil.toUpper(env),subject);
	}
	
	/**
	 * @param mailConfig the mailConfig to set
	 */
	public void setMailConfig(MailConfigBean mailConfig) {
		this.mailConfig = mailConfig;
	}

	/**
	 * @param mailSender the mailSender to set
	 */
	public void setMailSender(JavaMailSenderImpl mailSender) {
		this.mailSender = mailSender;
	}

}
